<!-- 立体轮播图组件 -->
<script lang="ts" setup>
const props = defineProps<{
  readonly imagesList: string[];
}>();

// 窗口大小为 7，前后补充6个素材就可以连贯起来
const supplyNum = 7 - 1;
const imglist = [...props.imagesList];
const len = props.imagesList.length;
imglist.unshift(...props.imagesList.slice(len - supplyNum, len));
imglist.push(...props.imagesList.slice(0, supplyNum));

const rotate = (i: number) => {
  // 中间的素材没有角度偏移 左右两边的偏移35度
  const val = i - options.mmiddleCur;
  return val < 0 ? 30 : val > 0 ? -30 : 0;
};

const options = reactive({
  // 是否启用动画过渡  这是首尾连接效果的关键
  isTrans: true,
  // 最中间素材的下标 初始值是第一张图片，但相对整个素材列表来说是第7个
  mmiddleCur: supplyNum,
  // 这里我们使用左外边距来实现平移的效果
  // 前面补了6个素材，隐掉了3个，所以初始左外边距是 负的 3个素材宽度
  // 240px (12.50vw) 是一个素材(.front)的宽度
  marginLeftCur: -3 * 12.5,
});

const timer = ref<number>();

/** 这一块逻辑比较绕，需要结合实操才能更好的理解 */
const toLeft = () => {
  // 当左滑到第四个素材时，下一个就进入末尾了
  if (options.mmiddleCur == 3) {
    // 我们在进入末尾之前，把动画过渡停掉
    options.isTrans = false;
    // 然后把素材(窗口最右侧)换成末尾之前的那个素材(其实素材是一样的，但是位置不一样)，也就是
    // 中间素材对应的是：素材列表减掉后补的6个素材，再往前推3个
    options.mmiddleCur = imglist.length - 6 - 3;
    // 左边距是：中间素材，再往前推4个，所有素材的宽度总和
    options.marginLeftCur = -(options.mmiddleCur + 1 - 4) * 12.5;
    // OK，到了这一步，虽然页面上没什么变化，但其实素材位置已经变了，已经连续上了
    // 接下来，我们正常走上一页的逻辑就好
    if (timer.value) clearTimeout(timer.value);
    // 这里 nextTick() 不好使，我们用一个定时器来延迟一下
    timer.value = setTimeout(() => last(), 0);
  } else last();
  function last() {
    options.isTrans = true;
    options.mmiddleCur--;
    options.marginLeftCur += 12.5;
  }
};
// 下一页的逻辑是差不多的
const toRight = () => {
  // 当右滑到倒数第四个素材时，下一个就进入开头了
  if (options.mmiddleCur == imglist.length - 1 - 3) {
    // 我们在进入开头之前，把动画过渡停掉
    options.isTrans = false;
    // 然后把素材(窗口最左侧)换成开头之前的那个素材，也就是
    // 中间素材对应的是：前补的6个素材，再往后推3个，也就是第9个素材
    options.mmiddleCur = 6 + 3 - 1; // 下标计算 -1
    // 左边距是：中间(第9个)素材，再往前推4个，总共5个素材的宽度总和
    options.marginLeftCur = -(options.mmiddleCur + 1 - 4) * 12.5;
    if (timer.value) clearTimeout(timer.value);
    timer.value = setTimeout(() => next(), 0);
  } else next();
  function next() {
    options.isTrans = true;
    options.mmiddleCur++;
    options.marginLeftCur -= 12.5;
  }
};

const inter = setInterval(toRight, 1500);

onUnmounted(() => {
  clearTimeout(timer.value);
  clearInterval(inter);
});

// h函数形式创建组件
const box = () => {
  return h("div", { class: "h-box" }, "鱼钓猫");
};
</script>

<template>
  <div class="carousel">
    <div
      class="background"
      :style="{ backgroundImage: `url(${imglist[options.mmiddleCur]})` }"
    ></div>
    <box></box>
    <div class="carousel-scroll">
      <div :class="['carousel-body', options.isTrans && 'trans']">
        <div class="carousel-item" v-for="(img, inx) in imglist" :key="inx">
          <div
            class="carousel-per"
            :style="{ transform: `rotateY(${rotate(inx)}deg)` }"
          >
            <div
              class="box front"
              ref="front"
              :style="{ backgroundImage: `url(${img})` }"
            ></div>
            <div
              class="box left"
              :style="{ backgroundImage: `url(${img})` }"
            ></div>
            <div
              class="box right"
              :style="{ backgroundImage: `url(${img})` }"
            ></div>
          </div>
        </div>
      </div>
    </div>
    <div class="btns">
      <div class="btn last" @click="toLeft"></div>
      <div class="btn next" @click="toRight"></div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.h-box {
  color: #fff;
  font-size: 24px;
  text-align: center;
  line-height: 3;
}

.carousel {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-position: center;
  background-size: 100%;
  transition: 1s;

  .background {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    // 高斯模糊
    filter: blur(3px);
    z-index: -1;
  }

  .carousel-scroll {
    width: 1720px; // 240 * 7 + 40
    margin: 100px auto 100px;
    padding: 100px 20px;
    box-shadow: 0 0 20px rgba($color: skyblue, $alpha: 0.5);
    overflow: hidden;

    .trans {
      transition: 0.5s ease-in-out;

      .carousel-per {
        transition: transform 0.5s ease-in-out;
      }
    }

    .carousel-body {
      display: flex;
      height: 100%;
      margin-left: v-bind("options.marginLeftCur + 'vw'");

      .carousel-item {
        perspective: 1200px;

        .carousel-per {
          position: relative;
          transform-style: preserve-3d;

          &:hover {
            .box {
              box-shadow: 0 0 50px rgba($color: #fff, $alpha: 0.7);
            }
          }

          .box {
            height: 100%;
            background-position: center;
            background-size: cover;
            border: 4px solid #fff;
            box-shadow: 0 0 50px rgba($color: pink, $alpha: 0.7);
          }

          .front {
            position: relative;
            width: 200px;
            height: 300px;
            margin: 0 20px;
            transition: transform 1s ease-in-out;
            transform-style: preserve-3d;

            &:after {
              content: "";
              position: absolute;
              bottom: -20%;
              width: 100%;
              height: 60px;
              background: #ffffff1c;
              box-shadow: 0px 0px 15px 5px #ffffff1c;
              transform: rotateX(-90deg) translate3d(0, 20px, 0px);
            }
          }

          .left,
          .right {
            position: absolute;
            top: 0;
            width: 40px;
          }

          .left {
            left: 0px;
            transform: translate3d(1px, 0, -20px) rotateY(-90deg);
          }

          .right {
            right: 0px;
            transform: translate3d(-1px, 0, -20px) rotateY(90deg);
          }
        }
      }
    }
  }

  .btns {
    display: flex;
    justify-content: center;

    .btn {
      width: 40px;
      height: 60px;
      margin: 0 100px;
      background-color: orangered;
      transition: 0.5s;
      cursor: pointer;

      &:hover {
        transform: scale(1.2);
      }
    }

    .last {
      clip-path: polygon(100% 0, 0 50%, 100% 100%, 60% 50%);
    }

    .next {
      clip-path: polygon(0 0, 100% 50%, 0 100%, 40% 50%);
    }
  }
}
</style>
